home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / VideoToolbox 96.06.15 / (Demos) / NoiseVBL.c < prev    next >
C/C++ Source or Header  |  1996-03-04  |  10KB  |  258 lines

  1. /*
  2. NoiseVBL.c
  3.  
  4. There are two ways to synchronize your program to a video display. Apple
  5. recommends using the vertical blanking level interrupt (VBL). That approach is
  6. illustrated here. The other approach, which I normally prefer, is to use a side
  7. effect of loading the video cards clut: most video drivers don't return until
  8. the vertical blanking interval. The interrupt approach is more or less
  9. guaranteed to work on all video cards, since Apple more or less requires that it
  10. be supported by all video drivers. Its only disadvantage is that 
  11. interrupts must be enabled. I have the impression--though I haven't collected
  12. hard data--that interrupts steal a lot of time, depending on how busy AppleTalk,
  13. the disk, SCSI, keyboard, mouse, and even miscellaneous INITs that are driven by
  14. VBL interrupts. So when I really want to crank, to display movies, I prefer to
  15. use SetPriority(7) to temporarily suspend all interrupts. In that case the
  16. video-driver side effect approach is your only choice. However, be warned that
  17. some video drivers don't wait, and others take more than a frame to load the
  18. clut, so do some careful timing before you rely on it, i.e. run TimeVideo.
  19.  
  20. This program shows a noise movie, a dynamic random checkerboard, at 66.7 Hz! 
  21. Each frame is displayed by calling CopyWindows.c. After showing each frame once
  22. NoiseVBL cycles through them again. Thus to get a true impression of white noise you
  23. need to compute lots of noise frames, e.g. 100. This may require several
  24. megabytes, depending what your screen's pixel depth is set to.
  25.  
  26. HISTORY:
  27. 11/23/88 Denis Pelli wrote it.
  28.  
  29. 4/20/91 I updated this. It now has essentially no hardware dependencies. It does
  30. require that the video be in a slot, since it uses slot interrupts. A basic
  31. problem with the scheme is that interrupts seem to be shut out while the
  32. interrupt service routines are running. As a result the timing information is
  33. inaccurate since many 1 ms interrupts are lost during the call to
  34. CopyBitsQuickly(). I think the only solution to this would be to put the time
  35. consuming call to CopyBitsQuickly back in the main program, waiting for the
  36. interrupt service routine to give it permission to start each new frame.
  37.  
  38. 4/15/92 Removed all bugs and updated to work with THINK C 5. Now properly set A5
  39. in the interrupt service routines, using the method suggested in Tech Note 180.
  40. I removed the old attempt to disable AppleTalk, which now seems to hang up when
  41. it attempts to reenable AppleTalk. I also removed the clut manipulations.
  42.  
  43. 4/16/92    Put the noise in its own window. Run essentially continuously until
  44. user quits.
  45.  
  46. 8/19/92 Replaced obsolete TimeIt.c by new Timer.c. Timing seems to be fine now.
  47.  
  48. 8/22/92 dgp Moved most of the interrupt service routine code to 
  49. VBLInterruptServiceRoutine.c. Just for fun, I made the program use an alternate monitor,
  50. if available. Fixed a small bug that prevented display of noise on an alternate
  51. monitor if it had a different pixelSize than the main screen.
  52.  
  53. 9/15/92 dgp Updated to use new Timer.c.
  54.  
  55. 2/18/93    dgp    Added fpu test.
  56. 2/20/93    dgp    Call Require(), increased stack space.
  57. 7/7/93    dgp replaced call to CopyBitsQuickly by CopyBits, for compatibility with
  58. Radius PowerView.
  59. 3/19/94    dgp updated the call to Shuffle(), adding new required argument.
  60. 5/30/94    dgp    call StackGrow() before calling NoiseVBL().
  61. 7/27/94 dgp changed "thePort" to &qd.thePort, for broader compatibility, as suggested by
  62. Marty Wachter.
  63. 9/5/94 dgp removed assumption in printf's that int==short.
  64. 11/6/94 dgp updated to demonstrate use of GWorld and CopyWindows().
  65. 6/14/95 dgp updated to use Microseconds() instead of Timer.c. GetNextEvent() had the wrong
  66.     eventMask: "keyDown" instead of "keyDownMask". 
  67. 11/10/95 dgp Improved the test for availability of the Microseconds trap, as suggested by
  68.         Apple's Mac Tech Q&A.
  69. */
  70. #include "VideoToolbox.h"
  71. #if UNIVERSAL_HEADERS
  72.     #include <LowMem.h>
  73. #else
  74.     #define LMGetMBarHeight() (* (short *) 0x0BAA)
  75.     #define LMSetMBarHeight(MBarHeightValue) ((* (short *) 0x0BAA) = (MBarHeightValue))
  76.     typedef unsigned long UInt32;
  77.     struct UnsignedWide {
  78.         UInt32 hi;
  79.         UInt32 lo;
  80.     };
  81.     typedef struct UnsignedWide UnsignedWide, *UnsignedWidePtr;
  82.     extern pascal void Microseconds(UnsignedWide *microTickCount)={0xA193, 0x225F, 0x22C8, 0x2280};
  83. #endif
  84.  
  85. /* The user may wish to adjust these. WIDTH should be a multiple of 4. */
  86. #define MAXFRAMES    200        // number of frames in movie
  87. #define WIDTH    256            /* width of displayed noise movie, in pixels */
  88. #define HEIGHT    256            /* height of displayed noise movie, in pixels */
  89. #define RANDOMPHASE 1        /* randomly shift each movie frame */
  90.  
  91. /* This is just for reference. The original is in VideoToolbox.h
  92. struct VBLTaskAndA5 {
  93.     volatile VBLTask vbl;
  94.     long ourA5;
  95.     void (*subroutine)(struct VBLTaskAndA5 *vblData);
  96.     GDHandle device;
  97.     long slot;
  98.     volatile long newFrame;                // Boolean
  99.     volatile long framesLeft;            // count down to zero
  100.     long framesDesired;
  101.     void *ptr;                            // use this for whatever you want
  102. };
  103. typedef struct VBLTaskAndA5 VBLTaskAndA5;
  104. */
  105.  
  106. void main(void);
  107. void NoiseVBL(void);
  108.  
  109. void main()
  110. {
  111.     long version;
  112.  
  113.     StackGrow(20000);
  114.     Require(gestalt8BitQD);
  115.     Gestalt(gestaltSystemVersion,&version);
  116.     #if !defined(_Microseconds)
  117.         #define _Microseconds 0xa193 // from traps.h
  118.     #endif
  119.     if(!TrapAvailable(_Microseconds))
  120.         PrintfExit("Sorry, I require the Microseconds trap, which is part of QuickTime and System 7.\n");
  121.     NoiseVBL();
  122. }
  123.  
  124. void NoiseVBL()
  125. {
  126.     int i,error,dx=32,dy=32,dt=1;
  127.     long frames;
  128.     unsigned long seed;
  129.     double s;
  130.     static char string[64];
  131.     Rect r;
  132.     short noiseI=0,noiseN=MAXFRAMES;
  133.     GWorldPtr noiseImage[MAXFRAMES];
  134.     short noiseSequence[MAXFRAMES]; /* shuffled sequence */
  135.     VBLTaskAndA5 noiseVBL;
  136.     WindowPtr window,oldPort;
  137.     EventRecord event;
  138.     int screen;
  139.     UnsignedWide microTicksStart,microTicks;
  140.     Boolean firstTime=1;
  141.     
  142.     assert(StackSpace()>5000);
  143.     GetDateTime(&seed);
  144.     srand(seed);
  145.     /* INITIALIZE QuickDraw */
  146.     #if (THINK_C || THINK_CPLUS || SYMANTEC_C)
  147.         console_options.nrows = 3;
  148.         printf("\n");
  149.     #elif __MWERKS__
  150.         SIOUXSettings.toppixel=LMGetMBarHeight()+19;    // allow for menu bar and title bar
  151.         SIOUXSettings.leftpixel=1;
  152.         SIOUXSettings.rows=3;
  153.         SIOUXSettings.autocloseonquit=0;
  154.         SIOUXSettings.showstatusline=0;
  155.         SIOUXSettings.asktosaveonclose=0;
  156.         printf("\n");
  157.     #else
  158.         InitGraf(&qd.thePort);
  159.         InitFonts();
  160.         InitWindows();
  161.         InitCursor();
  162.     #endif
  163.  
  164.     do{
  165.         if(GetScreenDevice(1)!=NULL)screen=ChooseScreen(1,"Which screen?");
  166.         else screen=0;
  167.         noiseVBL.device=GetScreenDevice(screen);
  168.     }while(noiseVBL.device==NULL);
  169.  
  170.     printf("Enter width of check in pixels (%d)?",dx);
  171.     gets(string);
  172.     sscanf(string,"%d",&dx);
  173.     printf("Enter height of check in pixels (%d)?",dy);
  174.     gets(string);
  175.     sscanf(string,"%d",&dy);
  176.     printf("Enter duration of check in frames (%d)?",dt);
  177.     gets(string);
  178.     sscanf(string,"%d",&dt);
  179.  
  180.     GetPort(&oldPort);
  181.     SetRect(&r,0,0,WIDTH,WIDTH);
  182.     CenterRectInRect(&r,&(*noiseVBL.device)->gdRect);
  183.     OffsetRect(&r,-(r.left%32),0);
  184.     window=NewCWindow(NULL,&r,"\pComputing noise ...",1,noGrowDocProc,(WindowPtr) -1L,0,0L);
  185.     SetPort(window);
  186.     for(i=0;i<MAXFRAMES;i++){
  187.         /* supply rect in global coordinates. The GWorld's pixels will be aligned with screen */
  188.         error=NewGWorld(&noiseImage[i],0,&r,NULL,NULL,keepLocal+useTempMem);
  189.         if(error)error=NewGWorld(&noiseImage[i],0,&r,NULL,NULL,keepLocal);
  190.         if(error)break;
  191.         error=NoiseFill(noiseImage[i],&noiseImage[i]->portRect,dx,dy,RANDOMPHASE);
  192.         LockPixels(noiseImage[i]->portPixMap);
  193.         if(error)break;
  194.     }
  195.     noiseN=i;
  196.     SetPort(oldPort);
  197.     printf("Using %d different frames of noise.\n",(int)noiseN);
  198.     for(i=0;i<noiseN;i++)noiseSequence[i]=i; /* initialize sequence */
  199.     FlushEvents(-1L,0);
  200.     printf("Hit any key to quit.\n");
  201.     FlushEvents(everyEvent,0);
  202.     SetWTitle(window,"\pNoiseVBL");
  203.     do{
  204.         SelectWindow(window);
  205.         Shuffle(noiseSequence,noiseN,sizeof(*noiseSequence));    /* shuffle the images */
  206.         noiseI=0;
  207.         noiseVBL.subroutine=NULL;    // use default
  208.         error=VBLInstall(&noiseVBL,noiseVBL.device,MAXFRAMES);
  209.         if(error)PrintfExit("VBLInstall: error %d\n",error);
  210.         noiseVBL.vbl.vblCount=1;    // Enable interrupt service routine
  211.         while(!noiseVBL.newFrame) ;    // wait for first frame
  212.         noiseVBL.newFrame=0;
  213.         while(!noiseVBL.newFrame) ;    // wait for second frame
  214.         Microseconds(µTicksStart);
  215.         frames=noiseVBL.framesLeft;
  216.         while(noiseVBL.framesLeft>1){
  217.             if(noiseVBL.newFrame){
  218.                 noiseVBL.newFrame=0;
  219.                 CopyWindows(noiseImage[noiseSequence[noiseI]],(CWindowPtr)window
  220.                     ,&noiseImage[0]->portRect,&window->portRect,srcCopy,NULL);
  221.                 if(noiseVBL.framesLeft%dt==0){
  222.                     noiseI++;
  223.                     if(noiseI>=noiseN) {
  224.                         Shuffle(noiseSequence,noiseN,sizeof(*noiseSequence));
  225.                         noiseI=0;
  226.                     }
  227.                 }
  228.             }
  229.         }
  230.         while(!noiseVBL.newFrame) ;    // wait for last frame
  231.         frames-=noiseVBL.framesLeft;
  232.         Microseconds(µTicks);
  233.         s=(microTicks.lo-microTicksStart.lo)
  234.             +(double)0x10000*0x10000*(microTicks.hi-microTicksStart.hi);
  235.         s/=1e6;
  236.         VBLRemove(&noiseVBL);
  237.         /*
  238.         The use of \n on the first time is a work around for a bug in the CodeWarrior 6.1
  239.         SIOUX console. It fails to update the screen when you send a \r, and only does so
  240.         when it receives a \n. Calling fflush(stdout) would force the screen to update, but
  241.         --another bug--also flushes stdin, discarding the user's keystrokes, which makes it
  242.         hard for the user to quit this program. So we use \n on the first time, so the user
  243.         gets to see at least the first timing result.
  244.         */
  245.         if(firstTime)printf("%.1f frames/s.\n",frames/s);
  246.         else printf("%.1f frames/s. \r",frames/s);
  247.         firstTime=0;
  248.     }while(!GetNextEvent(keyDownMask,&event));
  249.     FlushEvents(everyEvent,0);
  250.     for (i=0;i<noiseN;i++) DisposeGWorld(noiseImage[i]);
  251.     DisposeWindow(window);
  252.     #if (THINK_C || THINK_CPLUS || SYMANTEC_C)
  253.         abort();
  254.     #elif __MWERKS__
  255.         SIOUXSettings.autocloseonquit=1;
  256.     #endif
  257. }
  258.